001 /* 002 * Copyright 2005 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.depot; 020 021 import java.io.File; 022 import java.net.URL; 023 import java.net.URI; 024 import java.rmi.RMISecurityManager; 025 import java.util.ArrayList; 026 import java.util.Date; 027 028 import net.dpml.transit.Disposable; 029 import net.dpml.transit.Transit; 030 import net.dpml.transit.TransitError; 031 import net.dpml.transit.DefaultTransitModel; 032 import net.dpml.transit.model.TransitModel; 033 import net.dpml.transit.monitor.Adapter; 034 import net.dpml.transit.monitor.LoggingAdapter; 035 import net.dpml.transit.monitor.RepositoryMonitorAdapter; 036 import net.dpml.transit.monitor.CacheMonitorAdapter; 037 import net.dpml.transit.monitor.NetworkMonitorAdapter; 038 039 import net.dpml.lang.Enum; 040 import net.dpml.lang.PID; 041 import net.dpml.lang.Part; 042 043 import net.dpml.util.Logger; 044 import net.dpml.util.PropertyResolver; 045 046 /** 047 * CLI hander for the depot package. 048 * 049 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 050 * @version 1.0.1 051 */ 052 public final class Main //implements ShutdownHandler 053 { 054 private static Main m_MAIN; 055 private static final PID PROCESS_ID = new PID(); 056 057 private Object m_plugin; 058 private boolean m_debug = false; 059 private boolean m_trace = false; 060 061 /** 062 * Processes command line options to establish the command handler plugin to deploy. 063 * Command parameters recognixed by the console include the following: 064 * <ul> 065 * <li>-Ddpml.depot.addplication=transit|station|metro|build</li> 066 * <li>-debug</li> 067 * </ul> 068 * @param args the command line argument array 069 * @exception Exception if a error occurs 070 */ 071 public static void main( String[] args ) 072 throws Exception 073 { 074 if( null != m_MAIN ) 075 { 076 final String error = 077 "Console already established."; 078 throw new IllegalArgumentException( error ); 079 } 080 else 081 { 082 m_MAIN = new Main( args ); 083 } 084 } 085 086 private Main( String[] arguments ) 087 { 088 String[] args = processSystemProperties( arguments ); 089 090 // 091 // check for debug and trace cli options 092 // 093 094 if( CLIHelper.isOptionPresent( args, "-trace" ) ) 095 { 096 args = CLIHelper.consolidate( args, "-trace" ); 097 System.setProperty( "dpml.trace", "true" ); 098 m_trace = true; 099 } 100 101 if( CLIHelper.isOptionPresent( args, "-debug" ) ) 102 { 103 args = CLIHelper.consolidate( args, "-debug" ); 104 System.setProperty( "dpml.debug", "true" ); 105 m_debug = true; 106 } 107 108 // 109 // handle cli sub-system establishment 110 // 111 112 Command command = getCommand( args ); 113 if( Command.STATION.equals( command ) ) 114 { 115 handleStation( args ); 116 } 117 else 118 { 119 if( null == System.getProperty( "dpml.logging.config" ) ) 120 { 121 if( m_trace ) 122 { 123 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/trace" ); 124 } 125 else if( m_debug ) 126 { 127 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/debug" ); 128 } 129 else 130 { 131 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/default" ); 132 } 133 } 134 135 if( m_debug || m_trace ) 136 { 137 for( int i=0; i<arguments.length; i++ ) 138 { 139 getLogger().debug( "arg[" + i + "]: " + arguments[i] ); 140 } 141 } 142 143 if( Command.BUILD.equals( command ) ) 144 { 145 handleBuild( args ); 146 } 147 else if( Command.TRANSIT.equals( command ) ) 148 { 149 handleTransit( args ); 150 } 151 else if( Command.METRO.equals( command ) ) 152 { 153 handleMetro( args ); 154 } 155 else 156 { 157 final String error = 158 "Missing application key '" + APPLICATION_KEY + "'."; 159 System.err.println( error ); 160 System.exit( 1 ); 161 } 162 } 163 } 164 165 private void handleBuild( String[] args ) 166 { 167 if( getLogger().isTraceEnabled() ) 168 { 169 getLogger().trace( "launching builder" ); 170 } 171 String name = "build"; 172 String spec = "link:part:dpml/depot/dpml-library-build"; 173 handlePlugin( name, spec, args, false ); 174 } 175 176 private void handleMetro( String[] args ) 177 { 178 if( getLogger().isTraceEnabled() ) 179 { 180 getLogger().trace( "launching metro" ); 181 } 182 String name = "exec"; 183 String spec = "link:part:dpml/station/dpml-station-exec"; 184 handlePlugin( name, spec, args, true ); 185 } 186 187 private void handleTransit( String[] args ) 188 { 189 String name = "transit"; 190 String spec = "link:part:dpml/transit/dpml-transit-console"; 191 handlePlugin( name, spec, args, false ); 192 } 193 194 private void handleStation( String[] args ) 195 { 196 new File( Transit.DPML_DATA, "logs/station" ).mkdirs(); 197 if( CLIHelper.isOptionPresent( args, "-server" ) ) 198 { 199 if( m_trace ) 200 { 201 System.setProperty( "dpml.logging.level", "FINEST" ); 202 } 203 else if( m_debug ) 204 { 205 System.setProperty( "dpml.logging.level", "FINE" ); 206 } 207 if( getLogger().isTraceEnabled() ) 208 { 209 getLogger().trace( "launching station in server mode" ); 210 } 211 String name = "station"; 212 args = CLIHelper.consolidate( args, "-server" ); 213 String spec = "link:part:dpml/station/dpml-station-server"; 214 handlePlugin( name, spec, args, true ); 215 } 216 else 217 { 218 if( getLogger().isTraceEnabled() ) 219 { 220 getLogger().trace( "launching station in client mode" ); 221 } 222 String name = "station"; 223 String spec = "link:part:dpml/station/dpml-station-console"; 224 handlePlugin( name, spec, args, false ); 225 } 226 } 227 228 private void handlePlugin( String name, String spec, String[] args, boolean wait ) 229 { 230 System.setSecurityManager( new RMISecurityManager() ); 231 TransitModel model = getTransitModel( args ); 232 boolean waitForCompletion = deployHandler( model, name, spec, args, wait ); 233 if( !waitForCompletion ) 234 { 235 if( m_plugin instanceof Disposable ) 236 { 237 Disposable disposable = (Disposable) m_plugin; 238 disposable.dispose(); 239 } 240 if( model instanceof DefaultTransitModel ) 241 { 242 DefaultTransitModel disposable = (DefaultTransitModel) model; 243 disposable.dispose(); 244 } 245 System.exit( 0 ); 246 } 247 } 248 249 private boolean deployHandler( 250 TransitModel model, String command, String path, String[] args, boolean waitFor ) 251 { 252 Logger logger = getLogger(); 253 if( logger.isDebugEnabled() ) 254 { 255 logger.debug( "date: " + new Date() ); 256 logger.debug( "system: " + command ); 257 logger.debug( "uri: " + path ); 258 logger.debug( "args: [" + toString( args ) + "]" ); 259 logger.debug( "system classloader: [" 260 + System.identityHashCode( ClassLoader.getSystemClassLoader() ) 261 + "]" ); 262 } 263 Logger log = resolveLogger( logger, command ); 264 try 265 { 266 URI uri = new URI( path ); 267 Transit transit = Transit.getInstance( model ); 268 setupMonitors( transit, (Adapter) logger ); 269 270 Part part = Part.load( uri, true ); 271 m_plugin = 272 part.instantiate( 273 new Object[] 274 { 275 model, 276 args, 277 log 278 } 279 ); 280 } 281 catch( GeneralException e ) 282 { 283 getLogger().error( e.getMessage() ); 284 System.exit( 1 ); 285 } 286 catch( Exception e ) 287 { 288 Throwable cause = e.getCause(); 289 if( ( null != cause ) && ( cause instanceof GeneralException ) ) 290 { 291 getLogger().error( cause.getMessage() ); 292 System.exit( 1 ); 293 } 294 else 295 { 296 getLogger().error( e.getMessage(), e.getCause() ); 297 System.exit( 1 ); 298 } 299 } 300 catch( Throwable e ) 301 { 302 final String error = 303 "Deloyment failure." 304 + "\nTarget: " + command 305 + "\n URI: " + path; 306 getLogger().error( error, e ); 307 System.exit( 1 ); 308 } 309 310 if( m_plugin instanceof Runnable ) 311 { 312 getLogger().debug( "starting " + m_plugin.getClass().getName() ); 313 Thread thread = new Thread( (Runnable) m_plugin ); 314 thread.start(); 315 setShutdownHook( thread ); 316 return true; 317 } 318 else 319 { 320 getLogger().debug( "deployed " + m_plugin.getClass().getName() ); 321 return waitFor; 322 } 323 } 324 325 private Logger resolveLogger( Logger logger, String command ) 326 { 327 String partition = System.getProperty( "dpml.station.partition", null ); 328 if( null != partition ) 329 { 330 return new LoggingAdapter( partition ); 331 } 332 else 333 { 334 return logger.getChildLogger( command ); 335 } 336 } 337 338 private TransitModel getTransitModel( String[] args ) 339 { 340 final String key = "dpml.transit.model"; 341 String property = null; 342 for( int i=0; i<args.length; i++ ) 343 { 344 String arg = args[i]; 345 if( arg.startsWith( "-D" + key + "=" ) ) 346 { 347 property = arg.substring( 21 ); 348 break; 349 } 350 } 351 352 if( null != property ) 353 { 354 if( property.startsWith( "registry:" ) ) 355 { 356 try 357 { 358 return (TransitModel) new URL( property ).getContent( 359 new Class[]{TransitModel.class} ); 360 } 361 catch( Exception e ) 362 { 363 final String error = 364 "Unable to resolve registry reference: " + property; 365 throw new TransitError( error, e ); 366 } 367 } 368 else 369 { 370 final String error = 371 "System property value for the key ': " 372 + key 373 + "' contains an unrecognized value: " 374 + property; 375 throw new TransitError( error ); 376 } 377 } 378 379 // 380 // otherwise let Transit handle model creation 381 // 382 383 try 384 { 385 Logger logger = getLogger().getChildLogger( "transit" ); 386 return DefaultTransitModel.getDefaultModel( logger ); 387 } 388 catch( Exception e ) 389 { 390 final String error = 391 "Transit model establishment failure."; 392 throw new TransitError( error, e ); 393 } 394 } 395 396 private static Logger getLogger() 397 { 398 if( null == m_LOGGER ) 399 { 400 String category = System.getProperty( "dpml.logging.category", "depot" ); 401 m_LOGGER = new LoggingAdapter( java.util.logging.Logger.getLogger( category ) ); 402 } 403 return m_LOGGER; 404 } 405 406 private String toString( String[] args ) 407 { 408 StringBuffer buffer = new StringBuffer(); 409 for( int i=0; i<args.length; i++ ) 410 { 411 if( i > 0 ) 412 { 413 buffer.append( ", " ); 414 } 415 buffer.append( args[i] ); 416 } 417 return buffer.toString(); 418 } 419 420 private String[] processSystemProperties( String[] args ) 421 { 422 ArrayList result = new ArrayList(); 423 for( int i=0; i < args.length; i++ ) 424 { 425 String arg = args[i]; 426 int index = arg.indexOf( "=" ); 427 if( index > -1 && arg.startsWith( "-D" ) ) 428 { 429 String name = arg.substring( 2, index ); 430 String raw = arg.substring( index + 1 ); 431 String value = PropertyResolver.resolve( raw ); 432 System.setProperty( name, value ); 433 } 434 else 435 { 436 result.add( arg ); 437 } 438 } 439 return (String[]) result.toArray( new String[0] ); 440 } 441 442 //-------------------------------------------------------------------------- 443 // static utilities for setup of logging manager and root prefs 444 //-------------------------------------------------------------------------- 445 446 /** 447 * Setup the monitors. 448 */ 449 private static void setupMonitors( Transit instance, Adapter adapter ) throws Exception 450 { 451 instance.getRepositoryMonitorRouter().addMonitor( 452 new RepositoryMonitorAdapter( adapter ) ); 453 instance.getCacheMonitorRouter().addMonitor( 454 new CacheMonitorAdapter( adapter ) ); 455 instance.getNetworkMonitorRouter().addMonitor( 456 new NetworkMonitorAdapter( adapter ) ); 457 } 458 459 /** 460 * Create a shutdown hook that will trigger shutdown of the supplied plugin. 461 * @param thread the application thread 462 */ 463 public static void setShutdownHook( final Thread thread ) 464 { 465 // 466 // Create a shutdown hook to trigger clean disposal of the 467 // controller 468 // 469 470 Runtime.getRuntime().addShutdownHook( 471 new Thread() 472 { 473 public void run() 474 { 475 try 476 { 477 thread.interrupt(); 478 } 479 catch( Throwable e ) 480 { 481 boolean ignorable = true; 482 } 483 System.runFinalization(); 484 } 485 } 486 ); 487 } 488 489 /** 490 * DPML build key. 491 */ 492 private static final String BUILD_KEY = "dpml.build"; 493 494 /** 495 * The Depot system version. 496 */ 497 private static final String BUILD_ID = "1.0.1"; 498 499 static 500 { 501 setSystemProperty( "java.protocol.handler.pkgs", "net.dpml.transit" ); 502 setSystemProperty( "java.util.logging.config.class", "net.dpml.util.ConfigurationHandler" ); 503 setSystemProperty( "java.rmi.server.RMIClassLoaderSpi", "net.dpml.depot.DepotRMIClassLoaderSpi" ); 504 setSystemProperty( Transit.SYSTEM_KEY, Transit.DPML_SYSTEM.getAbsolutePath() ); 505 setSystemProperty( Transit.HOME_KEY, Transit.DPML_HOME.getAbsolutePath() ); 506 setSystemProperty( Transit.DATA_KEY, Transit.DPML_DATA.getAbsolutePath() ); 507 setSystemProperty( Transit.PREFS_KEY, Transit.DPML_PREFS.getAbsolutePath() ); 508 setSystemProperty( BUILD_KEY, BUILD_ID ); 509 } 510 511 private static void setSystemProperty( String key, String value ) 512 { 513 if( null == System.getProperty( key ) ) 514 { 515 System.setProperty( key, value ); 516 } 517 } 518 519 private static Logger m_LOGGER = null; 520 521 private Command getCommand( String[] args ) 522 { 523 String ref = getApplicationReference( args ); 524 String app = System.getProperty( APPLICATION_KEY, ref ); 525 return Command.parse( app ); 526 } 527 528 private String getApplicationReference( String[] args ) 529 { 530 String key = "-D" + APPLICATION_KEY + "="; 531 for( int i=0; i<args.length; i++ ) 532 { 533 String arg = args[i]; 534 if( arg.startsWith( key ) ) 535 { 536 return arg.substring( 25 ); 537 } 538 } 539 return null; 540 } 541 542 /** 543 * Application selection key. 544 */ 545 public static final String APPLICATION_KEY = "dpml.depot.application"; 546 547 /** 548 * Application identifier enumeration. 549 */ 550 private static final class Command extends Enum 551 { 552 static final long serialVersionUID = 1L; 553 554 /** 555 * Transit command id. 556 */ 557 public static final Command TRANSIT = new Command( "dpml.transit" ); 558 559 /** 560 * Metro command id. 561 */ 562 public static final Command METRO = new Command( "dpml.metro" ); 563 564 /** 565 * Station command id. 566 */ 567 public static final Command STATION = new Command( "dpml.station" ); 568 569 /** 570 * Builder command id. 571 */ 572 public static final Command BUILD = new Command( "dpml.builder" ); 573 574 /** 575 * Internal constructor. 576 * @param label the enumeration label. 577 */ 578 private Command( String label ) 579 { 580 super( label ); 581 } 582 583 /** 584 * Create a now mode using a supplied mode name. 585 * @param value the mode name 586 * @return the mode 587 * @exception NullPointerException if the supplied value is null 588 * @exception IllegalArgumentException if the supplied value is not recognized 589 */ 590 public static Command parse( String value ) throws NullPointerException, IllegalArgumentException 591 { 592 if( null == value ) 593 { 594 final String error = 595 "Undefined sub-system identifier." 596 + "\nThe depot cli handler must be supplied with an -D" 597 + APPLICATION_KEY + "=[id] where id is one of the value 'dpml.metro', " 598 + "'dpml.transit', 'dpml.station' or 'dpml.build'."; 599 throw new NullPointerException( error ); 600 } 601 if( value.equalsIgnoreCase( "dpml.metro" ) ) 602 { 603 return METRO; 604 } 605 else if( value.equalsIgnoreCase( "dpml.transit" ) ) 606 { 607 return TRANSIT; 608 } 609 else if( value.equalsIgnoreCase( "dpml.station" ) ) 610 { 611 return STATION; 612 } 613 else if( value.equalsIgnoreCase( "dpml.builder" ) ) 614 { 615 return BUILD; 616 } 617 else 618 { 619 final String error = 620 "Unrecognized application id [" + value + "]"; 621 throw new IllegalArgumentException( error ); 622 } 623 } 624 } 625 } 626